package ahct.log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import ahct.log.asynchronous.LogThread;

/**
 * 日志类 <一句话功能简述> <功能详细描述>
 * 
 * @author ChuTao
 * @version [版本号, 2013-6-15]
 * @see [相关类/方法]
 * @since [产品/模块版本] Copyright © lxchutao(楚涛) 2011-2015 lxchutao@163.com
 *        QQ:290363711 http://blog.csdn.net/chutao http://lxchutao.cnblogs.com
 */
public final class JLogger
{
	/**
	 * 当前日志级别设置（默认DEBUG）
	 */
	private LogLevel _cur_log_level = LogLevel.DEBUG;

	/**
	 * 日志文件路径+文件名（不含文件扩展名）
	 */
	private String _cur_log_file = "JLogger";

	/**
	 * 日志文件的最大长度（单位：字节） 默认1M（不得小于1024字节，即1K）
	 */
	private int _log_file_maxsize = 1048576;// 1M = 1*1024*1024=1048576

	/**
	 * 日志文件是否按命名空间分开标示，默认false。false:不分开，全部记录到一个文件；true:分开，按命名空间分开记录到各个文件。
	 */
	private boolean _cur_log_split = false;
	/**
	 * 当日志文件按package包名称分开存储时，包名称长度最大截取到第几分段（以“.”分割）(值必须大于0，方有效)，(可选)默认命名空间全称
	 */
	private int _log_pk_div_count_ = -1;

	/**
	 * 日志 异步写入标示（默认false） true使用异步方法写日志；false不使用异步方法写日志
	 */
	private boolean _log_asyn_ = false;

	/**
	 * 异步写入时，当日志队列为空的等待毫秒数
	 * （必须大于等于0，为0则没有等待时间(较耗资源)；可不配置，默认100毫秒）（_LOG_ASYN_必须配置TRUE，该项才起作用）
	 */
	private int _log_thread_wait = 100;

	/**
	 * 异步写入线程是否设置为守护线程。 （对应setDaemon(true);）
	 * TRUE守护线程，FALSE普通（默认FALSE）（_LOG_ASYN_必须配置TRUE，该项才起作用）
	 */
	private boolean _thread_is_daemon = true;
	/**
	 * 日志文件的保存时间（单位：天，整数-默认30天）（值大于零方有效）
	 */
	private int _log_file_save_time = 30;

	/**
	 * 日志文件的最大长度（单位：字节）（不得小于1024字节，即1K）
	 * 
	 * @return
	 * @see [类、类#方法、类#成员]
	 */
	public int getLogFileMaxsize()
	{
		return this._log_file_maxsize;
	}

	/**
	 * 日志 异步写入标示（默认false） true使用异步方法写日志；false不使用异步方法写日志
	 * 
	 * @return
	 * @see [类、类#方法、类#成员]
	 */
	final boolean isLogAsyn_()
	{
		return _log_asyn_;
	}

	private static JLogger _logger = null;

	private static final Lock _lock = new ReentrantLock();// 锁

	private final FileOperate _fileOperate;

	/**
	 * <默认构造函数>
	 */
	private JLogger()
	{
		Properties pro = new Properties();
		try
		{
			String pathPro = "JLogger.properties";
			String strLogCfgPathFile = System.getenv("_LOG_CONFIG_PATH_FILE_");
			File file = new File(pathPro);
			if (strLogCfgPathFile != null && !strLogCfgPathFile.trim().equals(""))
			{
				// file.isFile() //file.isDirectory()
				if (strLogCfgPathFile.endsWith(File.separator))
				{
					file = new File(strLogCfgPathFile + pathPro);
				}
				else
				{
					file = new File(strLogCfgPathFile);
				}
			}
            if (!file.exists())
            {
                file = new File(this.getClass().getResource("/").getPath() + File.separator + pathPro);
                System.out.println(file);
            }
            
            if (file.exists())
            {
                pro.load(new FileReader(file));
                _cur_log_level = LogLevelHelper.StrToLogLevel(pro.getProperty("_LOG_LEVEL_"));
                String logFile = pro.getProperty("_LOG_FILE_");
                String logFileMaxSize = pro.getProperty("_LOG_FILE_MAX_SIZE_");
                String logSplit = pro.getProperty("_LOG_SPLIT_");
                String logPKDivCount = pro.getProperty("_LOG_PK_DIV_COUNT_");
                String logAsyn = pro.getProperty("_LOG_ASYN_");
                String logAsynWait = pro.getProperty("_LOG_ASYN_WAIT_");
                String logAsynThreadIsDaemon = pro.getProperty("_LOG_ASYN_IS_DAEMON_");
                String logFileSaveTime_ = pro.getProperty("_LOG_FILE_SAVE_TIME_");
                
                if (logFile != null && !logFile.equals(""))
                {
                    _cur_log_file = logFile;
                }
                if (logFileMaxSize != null && !logFileMaxSize.equals(""))
                {
                    try
                    {
                        int size = Integer.parseInt(logFileMaxSize);
                        if (size > 1024)
                        {
                            _log_file_maxsize = size;
                        }
                    }
                    catch (Exception ex)
                    {
                        ex.printStackTrace();
                    }
                }
                if (logSplit != null && !logSplit.equals(""))
                {
                    if (logSplit.toUpperCase().equals("TRUE"))
                    {
                        this._cur_log_split = true;
                    }
                    
                    if (this._cur_log_split && logPKDivCount != null && !logPKDivCount.equals(""))
                    {
                        try
                        {
                            int count = Integer.parseInt(logPKDivCount);
                            if (count >= 0)
                            {
                                this._log_pk_div_count_ = count;
                            }
                        }
                        catch (Exception ex)
                        {
                            ex.printStackTrace();
                        }
                    }
                }
                if (logAsyn != null && !logAsyn.equals(""))
                {
                    if (logAsyn.toUpperCase().equals("TRUE"))
                    {
                        this._log_asyn_ = true;
                        if (logAsynWait != null && !logAsynWait.equals(""))
                        {
                            try
                            {
                                int wait = Integer.parseInt(logAsynWait);
                                if (wait >= 0)
                                {
                                    this._log_thread_wait = wait;
                                }
                            }
                            catch (Exception ex)
                            {
                                ex.printStackTrace();
                            }
                        }
                        if (logAsynThreadIsDaemon != null && !logAsynThreadIsDaemon.equals(""))
                        {
                            if (logAsynThreadIsDaemon.toUpperCase().equals("TRUE"))
                            {
                                _thread_is_daemon = true;
                            }
                        }
                        
                        LogThread.RunAsynchronous(this._log_thread_wait, this._thread_is_daemon);
                    }
                }
                if (logFileSaveTime_ != null && !logFileSaveTime_.equals(""))
                {
                    try
                    {
                        int temp = Integer.parseInt(logFileSaveTime_);
                        if (temp > 0)
                        {
                            _log_file_save_time = temp;
                        }
                    }
                    catch (Exception ex)
                    {
                        ex.printStackTrace();
                    }
                }
            }
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        
        _fileOperate = new FileOperate(_cur_log_file);
        if (_log_file_save_time > 0)
        {
            MonitorLogFileStorage thread = new MonitorLogFileStorage(_cur_log_file, _log_file_maxsize,
                _log_file_save_time);
            thread.setDaemon(this._thread_is_daemon);
            thread.start();
        }
    }
    
    /**
     * 获取当前日志实例对象(单实例)
     * 
     * @return 当前日志实例对象(单实例)
     * @see [类、类#方法、类#成员]
     */
    public static JLogger getInstance()
    {
        if (_logger == null)
        {
            _lock.lock(); // 取得锁
            if (_logger == null)
            {
                _logger = new JLogger();
            }
            _lock.unlock(); // 释放锁
        }
        return _logger;
    }
    
    public void Debug(String msg)
    {
        if (LogLevel.DEBUG.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.DEBUG, msg);
        }
    }
    
    public void debug(String msg)
    {
        if (LogLevel.DEBUG.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.DEBUG, msg);
        }
    }
    
    public void Debug(Object obj)
    {
        if (LogLevel.DEBUG.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.DEBUG, obj + "");
        }
    }
    
    public void debug(Object obj)
    {
        if (LogLevel.DEBUG.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.DEBUG, obj + "");
        }
    }
    
    public void Debug(String msg, Object obj)
    {
        if (LogLevel.DEBUG.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.DEBUG, msg + " " + obj);
        }
    }
    
    public void debug(String msg, Object obj)
    {
        if (LogLevel.DEBUG.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.DEBUG, msg + " " + obj);
        }
    }
    
    public void Info(String msg)
    {
        if (LogLevel.INFO.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.INFO, msg);
        }
    }
    
    public void info(String msg)
    {
        if (LogLevel.INFO.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.INFO, msg);
        }
    }
    
    public void Info(Object obj)
    {
        if (LogLevel.INFO.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.INFO, obj + "");
        }
    }
    
    public void info(Object obj)
    {
        if (LogLevel.INFO.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.INFO, obj + "");
        }
    }
    
    public void Info(String msg, Object obj)
    {
        if (LogLevel.INFO.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.INFO, msg + " " + obj);
        }
    }
    
    public void info(String msg, Object obj)
    {
        if (LogLevel.INFO.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.INFO, msg + " " + obj);
        }
    }
    
    public void Warn(String msg)
    {
        if (LogLevel.WARN.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.WARN, msg);
        }
    }
    
    public void warn(String msg)
    {
        if (LogLevel.WARN.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.WARN, msg);
        }
    }
    
    public void Warn(Object obj)
    {
        if (LogLevel.WARN.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.WARN, obj + "");
        }
    }
    
    public void warn(Object obj)
    {
        if (LogLevel.WARN.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.WARN, obj + "");
        }
    }
    
    public void Warn(String msg, Object obj)
    {
        if (LogLevel.WARN.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.WARN, msg + " " + obj);
        }
    }
    
    public void warn(String msg, Object obj)
    {
        if (LogLevel.WARN.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.WARN, msg + " " + obj);
        }
    }
    
    public void Error(String msg)
    {
        if (LogLevel.ERROR.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.ERROR, msg);
        }
    }
    
    public void error(String msg)
    {
        if (LogLevel.ERROR.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.ERROR, msg);
        }
    }
    
    public void Error(Object obj)
    {
        if (LogLevel.ERROR.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.ERROR, obj + "");
        }
    }
    
    public void error(Object obj)
    {
        if (LogLevel.ERROR.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.ERROR, obj + "");
        }
    }
    
    public void Error(String msg, Object obj)
    {
        if (LogLevel.ERROR.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.ERROR, msg + " " + obj);
        }
    }
    
    public void error(String msg, Object obj)
    {
        if (LogLevel.ERROR.ordinal() >= this._cur_log_level.ordinal())
        {
            this.Log(LogLevel.ERROR, msg + " " + obj);
        }
    }
    
    public void Fatal(String msg)
    {
        this.Log(LogLevel.FATAL, msg);
    }
    
    public void fatal(String msg)
    {
        this.Log(LogLevel.FATAL, msg);
    }
    
    public void Fatal(Object obj)
    {
        this.Log(LogLevel.FATAL, obj + "");
    }
    
    public void fatal(Object obj)
    {
        this.Log(LogLevel.FATAL, obj + "");
    }
    
    public void Fatal(String msg, Object obj)
    {
        this.Log(LogLevel.FATAL, msg + " " + obj);
    }
    
    public void fatal(String msg, Object obj)
    {
        this.Log(LogLevel.FATAL, msg + " " + obj);
    }
    
    /**
     * 记录内容到日志文件
     * 
     * @param level 日志级别
     * @param msg 待记录的日志内容
     * @see [类、类#方法、类#成员]
     */
    private void Log(LogLevel level, String msg)
    {
        String contents = this.LogInfoPrefix(level) + " " + msg;
        String logFileSuffix = "";
        if (this._cur_log_split)
        {
            logFileSuffix = this.LogFileSuffix();
        }
        this._fileOperate.AppendFile(logFileSuffix, contents, this._log_asyn_);
    }
    
    /**
     * 日志文件分开记录时的文件名后缀（非日志文件扩展名）
     * 
     * @return
     * @see [类、类#方法、类#成员]
     */
    private String LogFileSuffix()
    {
        String pk = "";
        StackTraceElement[] stacks = new Throwable().getStackTrace();
        StackTraceElement element = stacks[3];
        pk = element.getClassName();
        
        if (pk != null && pk.length() > 0)
        {
            if (this._log_pk_div_count_ > 0)
            {
                int index = 0;
                for (int i = 1; i <= this._log_pk_div_count_; i++)
                {
                    index = pk.indexOf(".", index + 1);
                    if (index == -1)
                    {
                        break;
                    }
                }
                if (index > 0)
                    pk = pk.substring(0, index);
            }
        }
        
        return "_" + pk;
    }
    
    /**
     * 日志前缀
     * 
     * @param level 日志级别
     * @return 日志前缀
     * @see [类、类#方法、类#成员]
     */
    private String LogInfoPrefix(LogLevel level)
    {
        StringBuffer sb = new StringBuffer();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss,SSS");
        
        StackTraceElement[] stacks = new Throwable().getStackTrace();
        StackTraceElement element = stacks[3];
        sb.append(format.format(new Date()))
            .append(" [" + level.toString() + "]")
            .append(" ")
            .append(element.getClassName())
            .append(":")
            .append(element.getMethodName())
            .append("::")
            .append(element.getLineNumber());
        
        return sb.toString();
    }
    
}
